#include "physicsobject.h"
#include "physicsworld.h"
#include "physicsmanager.h"

namespace gecko
{

PhysicsObject::PhysicsObject( PhysicsWorld * world, const BodyDef & def, const Vector3f & position )
{
	// create Box2D body definition
	b2BodyDef bodyDef;

	switch (def.BodyType)
	{
	case BodyDef::Type_Dynamic:
		bodyDef.type = b2_dynamicBody;
		break;

	case BodyDef::Type_Kinematic:
		bodyDef.type = b2_kinematicBody;
		break;

	case BodyDef::Type_Static:
		bodyDef.type = b2_staticBody;
		break;
	}

	bodyDef.position.Set(position.x, position.y);

	// create Box2D body
	m_body = world->GetBox2DWorld()->CreateBody(&bodyDef);

	// create shapes
	for (int i = 0; i < def.NumShapes; i++)
	{
		b2Shape * shape = NULL;

		switch (def.Shapes[i].Type)
		{
		case ShapeDef::Type_Circle:
			{
				shape = new b2CircleShape();
				((b2CircleShape*)shape)->m_radius = def.Shapes[i].Radius;
				b2Vec2 circlePos;
				PhysicsUtil::ConvertVector(def.Shapes[i].Offset, circlePos);
				((b2CircleShape*)shape)->m_p = circlePos;
				break;
			}

		case ShapeDef::Type_Poly:
			{
				// allocate vert array
				b2Vec2 * verts = new b2Vec2[def.Shapes[i].NumVerts];

				// copy verts from def
				for (int v = 0; v < def.Shapes[i].NumVerts; v++)
				{
					verts[v].Set(def.Shapes[i].PolyVerts[v].x, def.Shapes[i].PolyVerts[v].y);
				}

				// copy verts to shape
				shape = new b2PolygonShape();
				((b2PolygonShape*)shape)->Set(verts, def.Shapes[i].NumVerts);

				// delete temp verts
				delete [] verts;

				break;
			}

		default:
			break;
		}

		b2FixtureDef fixtureDef;
		fixtureDef.shape = shape;
		fixtureDef.density = def.Shapes[i].Density;
		fixtureDef.friction = def.Friction;
		fixtureDef.restitution = def.Restitution;

		m_body->CreateFixture(&fixtureDef);
		m_body->SetFixedRotation(def.FixedRotation);

		// cleanup
		delete shape;
	}
}

void PhysicsObject::GetPosition(Vector3f & position)
{
	const b2Vec2 & pos = m_body->GetPosition();
	PhysicsUtil::ConvertVector(pos, position);
}

void PhysicsObject::ApplyForce( const Vector3f & force, const Vector3f & worldPosition )
{
	b2Vec2 forceVec;
	b2Vec2 worldPos;

	PhysicsUtil::ConvertVector(force, forceVec);
	PhysicsUtil::ConvertVector(worldPosition, worldPos);

	m_body->ApplyForce(forceVec, worldPos);
}

void PhysicsObject::SetLinearVelocity( const Vector3f & velocity )
{
	b2Vec2 vel;
	PhysicsUtil::ConvertVector(velocity, vel);
	m_body->SetLinearVelocity(vel);
}

void PhysicsObject::GetLinearVelocity( Vector3f & velocity )
{
	b2Vec2 vel = m_body->GetLinearVelocity();
	PhysicsUtil::ConvertVector(vel, velocity);
}

void PhysicsObject::SetFriction( float friction )
{
	// for each fixture, set friction
	for (b2Fixture * fixture = m_body->GetFixtureList(); fixture != NULL; fixture = fixture->GetNext())
	{
		fixture->SetFriction(friction);
	}
}

void PhysicsObject::SetRestitution( float restitution )
{
	// for each fixture, set restitution
	for (b2Fixture * fixture = m_body->GetFixtureList(); fixture != NULL; fixture = fixture->GetNext())
	{
		fixture->SetRestitution(restitution);
	}
}

float PhysicsObject::GetRotation()
{
	return m_body->GetAngle();
}

}